package zheezes.jui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLWriter;

import zheezes.util.ZFileFilter;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.KeyStroke;
import java.awt.event.KeyEvent;
import java.awt.event.InputEvent;

public class HtmlEditor {
	public final static int WIDGET_EDIT_SCPN = 1;
	public final static int WIDGET_HTML_SCPN = 2;
	public final static int WIDGET_EDIT_PANE = 3;
	public final static int WIDGET_HTML_PANE = 4;
	
	private JFrame frame;
	private Object[] widgets = new Object[256];
	private AbstractAction[] actions = new AbstractAction[256];
	private JFileChooser htmlChooser = new JFileChooser();
	
	private final Action openFileAction = new OpenFileAction();
	private final Action saveFileAction = new SaveFileAction();
	private final Action setBoldAction = new SetBoldAction();
	private final Action exitAction = new ExitAction();
	
	private final ChangeListener tabPaneChangeListener = new TabPaneChangeListener();
	
	private HTMLEditorKit htmlKit = new HTMLEditorKit();
	private HTMLDocument htmlDoc = (HTMLDocument) htmlKit.createDefaultDocument();
	private File htmlFile = null;

	@SuppressWarnings("unused")
	private int lastSelPane = 0;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					HtmlEditor window = new HtmlEditor();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public HtmlEditor() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		JFrame.setDefaultLookAndFeelDecorated(false);
		
		frame = new JFrame();
		frame.setBounds(100, 100, 450, 300);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JMenuBar menuBar = new JMenuBar();
		frame.setJMenuBar(menuBar);
		
		JMenu mnFile = new JMenu("File");
		menuBar.add(mnFile);
		
		JMenuItem mntmOpen = new JMenuItem("Open");
		mntmOpen.setAction(openFileAction);
		mntmOpen.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_OPEN)));
		mnFile.add(mntmOpen);
		
		JMenuItem mntmSave = new JMenuItem("Save");
		mntmSave.setAction(saveFileAction);
		mntmSave.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_SAVE)));
		mnFile.add(mntmSave);
		
		JSeparator separator = new JSeparator();
		mnFile.add(separator);
		
		JMenuItem mntmExit = mnFile.add(exitAction);
		mntmExit.setText("Exit");
		
		JMenu mnEdit = new JMenu("Edit");
		menuBar.add(mnEdit);
		
		JMenuItem mntmSetBold = new JMenuItem("Bold");
		mntmSetBold.setAction(setBoldAction);
		mntmSetBold.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_BOLD)));
		mnEdit.add(mntmSetBold);
		
		JToolBar toolBar = new JToolBar();
		frame.getContentPane().add(toolBar, BorderLayout.NORTH);
		
		JButton btnOpenFile = new JButton();
		toolBar.add(btnOpenFile);
		btnOpenFile.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_OPEN)));
		btnOpenFile.addActionListener(openFileAction);
		
		JButton btnSaveFile = new JButton();
		toolBar.add(btnSaveFile);
		btnSaveFile.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_SAVE)));
		btnSaveFile.addActionListener(saveFileAction);
		
		JButton btnSetBold = new JButton();
		toolBar.add(btnSetBold);
		btnSetBold.setIcon(new ImageIcon(ResMgr.getImage(ResMgr.IMAGE_BOLD)));
		btnSetBold.addActionListener(setBoldAction);
		
		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
		tabbedPane.addChangeListener(tabPaneChangeListener);
		
		JScrollPane editScPane = new JScrollPane();
		tabbedPane.addTab("Design", null, editScPane, null);
		widgets[WIDGET_EDIT_SCPN] = editScPane;
		
		JTextPane editPane = new JTextPane();
		editScPane.setViewportView(editPane);
		widgets[WIDGET_EDIT_PANE] = editPane;
		
		editPane.setEditorKit(htmlKit);
		htmlDoc = (HTMLDocument) editPane.getDocument();
		
		JScrollPane htmlScPane = new JScrollPane();
		tabbedPane.addTab("Html", null, htmlScPane, null);
		widgets[WIDGET_HTML_SCPN] = htmlScPane;
		
		JTextPane htmlPane = new JTextPane();
		htmlScPane.setViewportView(htmlPane);
		widgets[WIDGET_HTML_PANE] = htmlPane;
		
		// htmlChooser.addChoosableFileFilter(new ExFileFilter(new String[] {".txt"})); 
		htmlChooser.addChoosableFileFilter(new ZFileFilter(new String[] {".html", ".htm"})); 
	}
	
	private Object getWidget(int name) {
		return getWidget(name, new Object());
	}
	
	@SuppressWarnings("unchecked")
	private <T> T getWidget(int name, T t) {
		if (name < 0 || name >= widgets.length) {
			return null;
		}
		return (T) widgets[name];
	}
	
	@SuppressWarnings("unused")
	private AbstractAction getAction(int name) {
		if (name < 0 || name >= actions.length) {
			return null;
		}
		return actions[name];
	}
	
	private void syncEditToHtml() {
		JTextPane editPane = getWidget(WIDGET_EDIT_PANE, new JTextPane());
		JTextPane htmlPane = getWidget(WIDGET_HTML_PANE, new JTextPane());
		HTMLDocument doc = (HTMLDocument) editPane.getDocument();
		StringWriter fw = new StringWriter();
		HTMLWriter writer = new HTMLWriter(fw, doc);
		try {
			writer.write();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		htmlPane.setText(fw.toString());
	}

	private void syncHtmlToEdit() {
		JTextPane htmlPane = getWidget(WIDGET_HTML_PANE, new JTextPane());
		StringReader reader = new StringReader(htmlPane.getText());
		try {
			htmlDoc.remove(0, htmlDoc.getLength());
			htmlKit.read(reader, htmlDoc, 0);
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}

	private int loadHtmlFile(File file) {
		if (file == null) {
			file = htmlFile;
		}
		try {
			FileReader reader = new FileReader(htmlFile);
			htmlKit.read(reader, htmlDoc, 0);
			reader.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
		
		return 0;
	}
	
	private int saveHtmlFile(File file) {
		if (file == null) {
			file = htmlFile;
		}
		if (file == null) {
			int ret = htmlChooser.showOpenDialog(frame);
			if (ret != JFileChooser.APPROVE_OPTION) {
				System.out.printf("File: %d\n", ret);
				return 1;
			}
			file = htmlChooser.getSelectedFile();
		}
		try {
			FileWriter fw = new FileWriter(file);
			// htmlKit.read(reader, htmlDoc, 0);
			HTMLWriter writer = new HTMLWriter(fw, htmlDoc);
			writer.write();
			fw.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
		
		return 0;
	}
	
	private void exit() {
		System.exit(0);
	}

	private class OpenFileAction extends AbstractAction {
		private static final long serialVersionUID = 1L;
		public OpenFileAction() {
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
			putValue(NAME, "Open");
			putValue(SHORT_DESCRIPTION, "Open file");
		}
		public void actionPerformed(ActionEvent e) {
			int ret = htmlChooser.showOpenDialog(frame);
			if (ret != JFileChooser.APPROVE_OPTION) {
				System.out.printf("File: %d\n", ret);
				return;
			}
			htmlFile = htmlChooser.getSelectedFile();
			if (htmlFile == null || !htmlFile.exists() || !htmlFile.canRead()) {
				return;
			}
			loadHtmlFile(htmlFile);
			try {
				frame.setTitle(htmlFile.toURI().toURL().toString());
			} catch (MalformedURLException e1) {
				e1.printStackTrace();
			}
		}
	}
	
	private class SaveFileAction extends AbstractAction {
		private static final long serialVersionUID = 1L;
		public SaveFileAction() {
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
			putValue(NAME, "Save");
			putValue(SHORT_DESCRIPTION, "Save file");
		}
		public void actionPerformed(ActionEvent e) {
			saveHtmlFile(null);
		}
	}
	
	private class SetBoldAction extends AbstractAction {
		private static final long serialVersionUID = 1L;
		public SetBoldAction() {
			putValue(NAME, "Bold");
			putValue(SHORT_DESCRIPTION, "Set bold");
		}
		public void actionPerformed(ActionEvent e) {
			JTextPane editPane = getWidget(WIDGET_EDIT_PANE, new JTextPane());
			HTMLDocument doc = (HTMLDocument) editPane.getDocument();
			int start = editPane.getSelectionStart();
			int end = editPane.getSelectionEnd();
			HTMLEditorKit kit = (HTMLEditorKit) editPane.getEditorKit();
			MutableAttributeSet attr = kit.getInputAttributes(); 
			boolean bold = (StyleConstants.isBold(attr)) ? false : true; 
			SimpleAttributeSet sas = new SimpleAttributeSet();
			StyleConstants.setBold(sas, bold);
			doc.setCharacterAttributes(start, end - start, sas, false);
			syncEditToHtml();
		}
	}
	
	private class ExitAction extends AbstractAction {
		private static final long serialVersionUID = 1L;
		public ExitAction() {
			putValue(NAME, "SwingAction");
			putValue(SHORT_DESCRIPTION, "Some short description");
		}
		public void actionPerformed(ActionEvent e) {
			saveHtmlFile(null);
			exit();
		}
	}

	private class TabPaneChangeListener implements ChangeListener {
		@Override
		public void stateChanged(ChangeEvent e) {
			JTabbedPane tabPane = (JTabbedPane) e.getSource();
			Component sel = tabPane.getSelectedComponent();
			int[] panes = { WIDGET_EDIT_SCPN, WIDGET_HTML_SCPN };
			for (int p: panes) {
				Component c = (Component) getWidget(p);
				if (sel != c) {
					continue;
				}
				System.out.println("get: " + p);
				lastSelPane = p;
				switch (p) {
				case WIDGET_EDIT_SCPN:
					syncHtmlToEdit();
					break;
				case WIDGET_HTML_SCPN:
					syncEditToHtml();
					break;
				}
			}
		}
	}
}
